﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NSsh.Server.Configuration;
using NSsh.Common.Utility;
using System.Net;
using NSsh.Server.TransportLayer;
using NSsh.Server.TransportLayer.State;
using NSsh.Server.Services;
using NSsh.Server.ChannelLayer;
using NSsh.Common.Packets;
using NSsh.Common.Packets.Channel;
using System.Security.Principal;
using System.Reflection;
using System.Threading;
using System.IO;
using log4net;
using System.Diagnostics;

namespace NSsh.Server.Console
{
    class Program
    {
        private static ILog log = LogManager.GetLogger(typeof(Program));

        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "ServerMain";

            // configure logging
            log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo("logging.xml"));
            Trace.Listeners.Add(new Log4netListener());

            // Setup exception handler
            AppDomain.CurrentDomain.UnhandledException += UnhandledException;

            // output our platform details
            log.Info("NSsh Server version: [" + Assembly.GetExecutingAssembly().GetName().Version + "]");
            log.Info("Running as: [" + WindowsIdentity.GetCurrent().Name + "]");
            log.Debug("Platform: [" + Environment.OSVersion.Platform + "]");
            log.Debug("Version: [" + Environment.OSVersion.Version + "]");
            log.Debug("OS: [" + Environment.OSVersion.ToString() + "]");
            log.Debug("Environment: [" + Environment.Version.ToString() + "]");
            log.Debug("Current Dir: [" + Environment.CurrentDirectory + "]");
            
            NSshServiceConfiguration configuration = NSshServiceConfiguration.LoadFromFile("NSshConfiguration.xml");
            SetupDependencies(configuration);

            ISshService service = Dependency.Resolve<ISshService>();
            ((NSshService)service).Start();

            System.Console.ReadLine();
        }

        static void UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            log.Fatal("Unhandled exception in NSsh.", e.ExceptionObject as Exception);
        }

        private static void SetupDependencies(NSshServiceConfiguration configuration)
        {
            Dependency.RegisterInstance<NSshServiceConfiguration>("NSshServiceConfiguration", configuration);
            Dependency.RegisterSingleton<ISshService, NSshService>("NSshService");
            Dependency.RegisterTransient<ISshSession, SshSession>("SshSession");
            Dependency.RegisterTransient<ITransportLayerManager, TransportLayerManager>("TransportLayerManager");
            Dependency.RegisterTransient<AbstractTransportState, ConnectedState>(TransportLayerState.Connected.ToString());
            Dependency.RegisterTransient<AbstractTransportState, VersionsExchangedState>(TransportLayerState.VersionsExchanged.ToString());
            Dependency.RegisterTransient<AbstractTransportState, KeysExchangedState>(TransportLayerState.KeysExchanged.ToString());
            Dependency.RegisterTransient<AbstractTransportState, AuthenticatedState>(TransportLayerState.Authenticated.ToString());
            Dependency.RegisterSingleton<IKeySetupService, KeySetupService>("KeySetupService");
            Dependency.RegisterSingleton<ISecureRandom, SecureRandom>("SecureRandom");
            Dependency.RegisterSingleton<ICipherFactory, CipherFactory>("CipherFactory");
            Dependency.RegisterSingleton<IMacFactory, MacFactory>("MacFactory");
            Dependency.RegisterSingleton<IPasswordAuthenticationService, PasswordAuthenticationService>("PasswordAuthenticationService");
            Dependency.RegisterSingleton<IPublicKeyAuthenticationService, PublicKeyAuthenticationService>("PublicKeyAuthenticationService");
            Dependency.RegisterTransient<IChannel, Channel>("Channel");
            Dependency.RegisterTransient<IChannelConsumer, PtyChannelConsumer>(ChannelRequestType.PseudoTerminal.ToString());
            Dependency.RegisterTransient<IChannelConsumer, PtyChannelConsumer>(ChannelRequestType.Shell.ToString());            
            Dependency.RegisterTransient<IChannelCommandConsumer, CommandChannelConsumer>(ChannelRequestType.ExecuteCommand.ToString());
            //Dependency.RegisterSingleton<IImpersonationProvider, ImpersonationProvider>("ImpersonationProvider");
            Dependency.RegisterSingleton<IImpersonationProvider, BasicImpersonationProvider>("ImpersonationProvider");
            Dependency.RegisterSingleton<IPacketFactory, PacketFactory>("PacketFactory");
        }
    }
}
